package IC;

import java.io.FileReader;

import java.io.IOException;
import java.util.List;

import java_cup.runtime.Symbol;

import IC.AST.*;
import IC.Parser.*;
import IC.SEMANTIC.SemanticAnalysisVisitor;
import IC.SEMANTIC.CreateTypeTableVisitor;
import IC.SEMANTIC.InitializationTestVisitor;
import IC.SEMANTIC.SemanticWarning;

public class Compiler {

	private static void errorOut() {
		System.out.println("Illigal arguments\nUsage: java IC.Compiler <file.ic> [-L<path to libic.sig>] [-print-ast]");
	}

	private static void syntaxErrorOut(List<SyntaxError> errors) {
		for (SyntaxError error : errors) {
			System.out.println(error);
		}
	}

	public static void main(String[] args) {
		if (args.length == 0) { // check arguments correctness
			errorOut();
			return;
		}
		String icFile = null, libFile = null;
		boolean boolAST = false;
		boolean printSymbolTypeTables = false;

		//parse arguments
		for (int i = 0 ; i < args.length ; i++) {
			if (args[i].startsWith("-L"))
				libFile = args[i].substring(2);
			else if ("-print-ast".equalsIgnoreCase(args[i]))
				boolAST = true;
			else if ("-dump-symtab".equalsIgnoreCase(args[i]))
				printSymbolTypeTables = true;
				
			else if (!args[i].startsWith("-"))
				icFile = args[i];
			else {
				errorOut();
				return;
			}
		}
		//check IC file correctness
		if (icFile == null) {
			System.out.println("IC file missing");
			errorOut();
			return;
		}

		FileReader icReader = null;
		try {
			icReader = new FileReader(icFile);
		} catch (IOException e) {
			System.out.println("Error openning IC file: "+icFile);
			return;
		}

		ICClass libClass = null;
		if (libFile != null) {
			FileReader libReader = null;
			try {
				libReader = new FileReader(libFile);
			} catch (IOException e) {
				System.out.println("Error openning library file: "+libFile);
				return;
			}

			//parse library file
			LibraryParser libParser = new LibraryParser(new Lexer(libReader));
			try {
				Symbol res = libParser.parse();
				if (libParser.GetSyntaxErrorsList().size() != 0) {
					syntaxErrorOut(libParser.GetSyntaxErrorsList());
					return;
				}
				libClass = (ICClass)res.value;
			} catch (Exception e) {
				System.out.println(e);
				return;
			}
		}

		//parse IC file
		Parser parser = new Parser(new Lexer(icReader));
		Program program = null; 
		try {
			Symbol res = parser.parse();
			if (parser.GetSyntaxErrorsList().size() > 0) {
				syntaxErrorOut(parser.GetSyntaxErrorsList());
				return;
			}
			program = (Program)res.value;	
		} catch (Exception e) {
			System.out.println(String.format("Error While parsing the file %s: %s", icFile ,e.getMessage()));
			e.printStackTrace();
			return;
		}
		
		if (libClass != null)
			program.ImportClass(libClass);
		
		
		//creating the type table
		CreateTypeTableVisitor typeTableVisitor = new CreateTypeTableVisitor(icFile);
		program.accept(typeTableVisitor);
		if (typeTableVisitor.getSemanticError()!=null) {
			System.out.println(typeTableVisitor.getSemanticError());
			return;
		}
		
		//print the symbol and type table if requested
		if (printSymbolTypeTables)
			System.out.println(typeTableVisitor.getTypeTable().toString());
		
        SemanticAnalysisVisitor semanticVisitor = new SemanticAnalysisVisitor(typeTableVisitor.getTypeTable(), icFile);
        program.accept(semanticVisitor);
        if (semanticVisitor.getError()!=null) {
            System.out.println(semanticVisitor.getError());
            return;
        }

        InitializationTestVisitor itv = new InitializationTestVisitor(typeTableVisitor.getTypeTable());
        program.accept(itv);
        for (SemanticWarning cw : itv.getSemanticWarningsList())
            System.out.println(cw);
       
    	if (boolAST) {
    		PrettyPrinter progPP = new PrettyPrinter(icFile);
    		System.out.println(program.accept(progPP));
    	}
        
        if (printSymbolTypeTables)
            System.out.println(semanticVisitor.getGlobalSymTable().toString());
		
		//print if requested
		if (boolAST) {
			if (libClass != null) {
				PrettyPrinter libPP = new PrettyPrinter(libFile);
				System.out.println(libClass.accept(libPP));
			}
			PrettyPrinter programPP = new PrettyPrinter(icFile);
			System.out.println(program.accept(programPP));
		}
	
	}   
}